/*
 *Copyright 2021-2023 NERCIS
 *
 *Licensed under the Apache License, Version 2.0 (the "License");
 *you may not use this file except in compliance with the License.
 *You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 *Unless required by applicable law or agreed to in writing, software
 *distributed under the License is distributed on an "AS IS" BASIS,
 *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *See the License for the specific language governing permissions and
 *limitations under the License.
 */

/*
 *Copyright 2021-2023 NERCIS
 *
 *Licensed under the Apache License, Version 2.0 (the "License");
 *you may not use this file except in compliance with the License.
 *You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 *Unless required by applicable law or agreed to in writing, software
 *distributed under the License is distributed on an "AS IS" BASIS,
 *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *See the License for the specific language governing permissions and
 *limitations under the License.
 */

package cn.ac.nercis.pes.service.utils;

import cn.ac.nercis.pes.common.utils.JsonUtils;
import cn.ac.nercis.pes.model.bo.doc.common.DocRequest;
import cn.ac.nercis.pes.model.bo.doc.common.DocResult;
import cn.ac.nercis.pes.model.bo.doc.common.evaluation.DocMessageType;
import cn.ac.nercis.pes.model.dto.common.Result;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.util.Arrays;
import org.zeromq.SocketType;
import org.zeromq.ZMQ;

import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 文档生成服务辅助类
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
public final class DocServerUtils {
    private static final String WIN_DOC_SERVER_APP = "doc-win/doc-server.exe";
    private static final Integer IO_THREAD_COUNT = 3;
    private static final String ADDRESS = "ipc://nercis-document-server";
    private static final Integer TEST_CONNECT_TIMEOUT = 3;
    private static final AtomicBoolean CONNECT_STATE = new AtomicBoolean(false);

    private DocServerUtils(){}

    public static ProcessBuilder startDocServer(){
        var appFile = FileUtils.getFile(FileManage.getPlugDir(),WIN_DOC_SERVER_APP);
        if(!appFile.exists()){
            return null;
        }
        var processBuilder = new ProcessBuilder();
        processBuilder.directory(appFile.getParentFile());
        processBuilder.command(appFile.getAbsolutePath());
        return processBuilder;
    }

    public static boolean testConnect(){
        ExecutorService executor;
        try{
            executor = Executors.newSingleThreadExecutor();
            executor.submit(DocServerUtils::runConnect);
            executor.shutdown();
            var isTimeout = executor.awaitTermination(TEST_CONNECT_TIMEOUT,TimeUnit.SECONDS);
            if(!isTimeout){
                CONNECT_STATE.set(false);
            }
        }catch (Exception ex){
            log.error("test error：",ex);
        }
        return CONNECT_STATE.get();
    }

    public static boolean isConnect(){
        return CONNECT_STATE.get();
    }

    public static Result<String> generateDocument(@NonNull DocRequest request){
        String requestJson = JsonUtils.toJson(request);
        try(ZMQ.Context context = ZMQ.context(IO_THREAD_COUNT);
            ZMQ.Socket socket = context.socket(SocketType.REQ)){
            socket.connect(ADDRESS);
            socket.send(StringUtils.getBytes(requestJson, StandardCharsets.UTF_8));
            var res = response(socket);
            if(Objects.nonNull(res) && res.isSuccess()){
                return Result.success();
            }else{
                if(Objects.nonNull(res)){
                    log.warn("生成文档失败：{}",res);
                    return Result.failed(res.getMsg());
                }
                return Result.failed();
            }
        }catch (Exception ex){
            log.error("生成文档出错：",ex);
            return Result.failed();
        }
    }

    private static void runConnect(){
        try(ZMQ.Context context = ZMQ.context(IO_THREAD_COUNT);
            ZMQ.Socket socket = context.socket(SocketType.REQ)){
            socket.connect(ADDRESS);
            var request = getSupportListRequest();
            socket.send(StringUtils.getBytes(request, StandardCharsets.UTF_8));
            var res = response(socket);
            CONNECT_STATE.set(Objects.nonNull(res) && res.isSuccess());
        }catch (Exception ex){
            CONNECT_STATE.set(false);
        }
    }

    private static DocResult response(ZMQ.Socket socket){
        var responseBytes = socket.recv();
        if(Arrays.isNullOrEmpty(responseBytes)){
            return DocResult.failed("连接到文档生成服务失败");
        }
        var content = StringUtils.toEncodedString(responseBytes,StandardCharsets.UTF_8);
        return JsonUtils.fromJson(content,DocResult.class);
    }

    private static String getSupportListRequest(){
        var req = DocRequest.builder().messageType(DocMessageType.SUPPORT_DOCUMENT.getType()).build();
        return JsonUtils.toJson(req);
    }
}
